import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams, useLocation } from 'react-router-dom';
import { RiCloseLine, RiLoader2Line } from '@remixicon/react';
import Recorder from 'js-audio-recorder';
import { useRafInterval } from 'ahooks';
import { convertToMp3 } from './utils';
import s from './index.module.css';
import cn from '@/utils/classnames';
import { StopCircle } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices';
import { audioToText } from '@/service/share';

type VoiceInputTypes = {
  onConverted: (text: string) => void;
  onCancel: () => void;
  wordTimestamps?: string;
};

const VoiceInput = ({ onCancel, onConverted, wordTimestamps }: VoiceInputTypes) => {
  const { t } = useTranslation();
  const recorder = useRef(
    new Recorder({
      sampleBits: 16,
      sampleRate: 16000,
      numChannels: 1,
      compiling: false,
    }),
  );
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const ctxRef = useRef<CanvasRenderingContext2D | null>(null);
  const drawRecordId = useRef<number | null>(null);
  const [originDuration, setOriginDuration] = useState(0);
  const [startRecord, setStartRecord] = useState(false);
  const [startConvert, setStartConvert] = useState(false);
  const { pathname } = useLocation();
  const params = useParams();
  const clearInterval = useRafInterval(() => {
    setOriginDuration(originDuration + 1);
  }, 1000);

  const drawRecord = useCallback(() => {
    drawRecordId.current = requestAnimationFrame(drawRecord);
    const canvas = canvasRef.current!;
    const ctx = ctxRef.current!;
    const dataUnit8Array = recorder.current.getRecordAnalyseData();
    const dataArray = [].slice.call(dataUnit8Array);
    const lineLength = parseInt(`${canvas.width / 3}`);
    const gap = parseInt(`${1024 / lineLength}`);

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    let x = 0;
    for (let i = 0; i < lineLength; i++) {
      let v =
        dataArray.slice(i * gap, i * gap + gap).reduce((prev: number, next: number) => {
          return prev + next;
        }, 0) / gap;

      if (v < 128) v = 128;
      if (v > 178) v = 178;
      const y = ((v - 128) / 50) * canvas.height;

      ctx.moveTo(x, 16);
      if (ctx.roundRect) ctx.roundRect(x, 16 - y, 2, y, [1, 1, 0, 0]);
      else ctx.rect(x, 16 - y, 2, y);
      ctx.fill();
      x += 3;
    }
    ctx.closePath();
  }, []);
  const handleStopRecorder = useCallback(async () => {
    clearInterval();
    setStartRecord(false);
    setStartConvert(true);
    recorder.current.stop();
    drawRecordId.current && cancelAnimationFrame(drawRecordId.current);
    drawRecordId.current = null;
    const canvas = canvasRef.current!;
    const ctx = ctxRef.current!;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    const mp3Blob = convertToMp3(recorder.current);
    const mp3File = new File([mp3Blob], 'temp.mp3', { type: 'audio/mp3' });
    const formData = new FormData();
    formData.append('file', mp3File);
    formData.append('word_timestamps', wordTimestamps || 'disabled');

    let url = '';
    let isPublic = false;

    if (params.token) {
      url = '/audio-to-text';
      isPublic = true;
    } else if (params.appId) {
      if (pathname.search('explore/installed') > -1) url = `/installed-apps/${params.appId}/audio-to-text`;
      else url = `/apps/${params.appId}/audio-to-text`;
    }

    try {
      const audioResponse = await audioToText(url, isPublic, formData);
      onConverted(audioResponse.text);
      onCancel();
    } catch (e) {
      onConverted('');
      onCancel();
    }
  }, [clearInterval, onCancel, onConverted, params.appId, params.token, pathname, wordTimestamps]);
  const handleStartRecord = async () => {
    try {
      await recorder.current.start();
      setStartRecord(true);
      setStartConvert(false);

      if (canvasRef.current && ctxRef.current) drawRecord();
    } catch (e) {
      onCancel();
    }
  };

  const initCanvas = () => {
    const dpr = window.devicePixelRatio || 1;
    const canvas = document.getElementById('voice-input-record') as HTMLCanvasElement;

    if (canvas) {
      const { width: cssWidth, height: cssHeight } = canvas.getBoundingClientRect();

      canvas.width = dpr * cssWidth;
      canvas.height = dpr * cssHeight;
      canvasRef.current = canvas;

      const ctx = canvas.getContext('2d');
      if (ctx) {
        ctx.scale(dpr, dpr);
        ctx.fillStyle = 'rgba(209, 224, 255, 1)';
        ctxRef.current = ctx;
      }
    }
  };
  if (originDuration >= 600 && startRecord) handleStopRecorder();

  useEffect(() => {
    initCanvas();
    handleStartRecord();
    const recorderRef = recorder?.current;
    return () => {
      recorderRef?.stop();
    };
  }, []);

  const minutes = parseInt(`${parseInt(`${originDuration}`) / 60}`);
  const seconds = parseInt(`${originDuration}`) % 60;

  return (
    <div className={cn(s.wrapper, 'absolute inset-0 rounded-xl')}>
      <div className="absolute inset-[1.5px] flex items-center pl-[14.5px] pr-[6.5px] py-[14px] bg-primary-25 rounded-[10.5px] overflow-hidden">
        <canvas id="voice-input-record" className="absolute left-0 bottom-0 w-full h-4" />
        {startConvert && <RiLoader2Line className="animate-spin mr-2 w-4 h-4 text-primary-700" />}
        <div className="grow">
          {startRecord && <div className="text-sm text-gray-500">{t('common.voiceInput.speaking')}</div>}
          {startConvert && <div className={cn(s.convert, 'text-sm')}>{t('common.voiceInput.converting')}</div>}
        </div>
        {startRecord && (
          <div
            className="flex justify-center items-center mr-1 w-8 h-8 hover:bg-primary-100 rounded-lg  cursor-pointer"
            onClick={handleStopRecorder}
          >
            <StopCircle className="w-5 h-5 text-primary-600" />
          </div>
        )}
        {startConvert && (
          <div className="flex justify-center items-center mr-1 w-8 h-8 hover:bg-gray-200 rounded-lg  cursor-pointer" onClick={onCancel}>
            <RiCloseLine className="w-4 h-4 text-gray-500" />
          </div>
        )}
        <div
          className={`w-[45px] pl-1 text-xs font-medium ${originDuration > 500 ? 'text-[#F04438]' : 'text-gray-700'}`}
        >{`0${minutes.toFixed(0)}:${seconds >= 10 ? seconds : `0${seconds}`}`}</div>
      </div>
    </div>
  );
};

export default VoiceInput;
